// totp.ts

// Node.js 类型声明
declare const require: any;
declare const module: any;
declare const Buffer: any;

/**
 * 将 Base32 编码的字符串解码为 Uint8Array。
 * @param base32 - Base32 编码的字符串。
 * @returns 解码后的字节数组。
 */
function base32ToUint8Array(base32: string): Uint8Array {
    const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    base32 = base32.toUpperCase().replace(/=+$/, ''); // 去除填充符
  
    const bitsPerChar = 5;
    const output = new Uint8Array(Math.ceil((base32.length * bitsPerChar) / 8));
  
    let bitBuffer = 0;
    let bitsInBuffer = 0;
    let outputIndex = 0;
  
    for (let i = 0; i < base32.length; i++) {
      const charIndex = alphabet.indexOf(base32[i]);
      if (charIndex === -1) {
        throw new Error('Invalid Base32 character found: ' + base32[i]);
      }
  
      bitBuffer = (bitBuffer << bitsPerChar) | charIndex;
      bitsInBuffer += bitsPerChar;
  
      while (bitsInBuffer >= 8) {
        bitsInBuffer -= 8;
        output[outputIndex++] = (bitBuffer >> bitsInBuffer) & 0xFF;
      }
    }
    return output;
  }
  
  /**
   * 生成指定时间点的 TOTP 验证码。
   * @param secret - Base32 编码的密钥。
   * @param timestamp - 用于生成验证码的时间戳 (毫秒)。
   * @param options - 配置选项。
   * @returns 生成的 TOTP 验证码。
   */
  async function generateTOTP(
    secret: string, 
    timestamp: number = Date.now(), 
    options: { interval?: number; digits?: number } = {}
  ): Promise<string> {
    const { interval = 30, digits = 6 } = options;
  
    // 1. 将 Base32 密钥解码为 Uint8Array
    const key = base32ToUint8Array(secret);
  
    // 2. 计算时间步长 (T)
    // T = Math.floor(timestamp / (interval * 1000))
    let timeStep = Math.floor(timestamp / (interval * 1000));
  
    // 3. 将时间步长转换为 8 字节的大端序 (Big-Endian) Uint8Array
    const timeArray = new Uint8Array(8);
    for (let i = 7; i >= 0; i--) {
      timeArray[i] = timeStep & 0xFF;
      timeStep >>= 8;
    }
  
    // 4. 使用 Web Crypto API 计算 HMAC-SHA1
    //    Node.js 环境下需要引入 crypto 模块
    let hmacDigest;
    if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) {
      // --- 浏览器环境 ---
      const cryptoKey = await window.crypto.subtle.importKey(
        'raw',
        key,
        { name: 'HMAC', hash: 'SHA-1' },
        false,
        ['sign']
      );
      hmacDigest = await window.crypto.subtle.sign('HMAC', cryptoKey, timeArray);
    } else if (typeof require !== 'undefined') {
      // --- Node.js 环境 ---
      const crypto = require('crypto');
      const hmac = crypto.createHmac('sha1', Buffer.from(key));
      hmac.update(Buffer.from(timeArray));
      hmacDigest = hmac.digest();
    } else {
      throw new Error('当前环境不支持所需的加密 API。请在现代浏览器或 Node.js 中运行。');
    }
    
    const digestArray = new Uint8Array(hmacDigest);
  
    // 5. 动态截断 (Dynamic Truncation)
    const offset = digestArray[digestArray.length - 1] & 0x0F;
    let code = (
      ((digestArray[offset] & 0x7F) << 24) |
      ((digestArray[offset + 1] & 0xFF) << 16) |
      ((digestArray[offset + 2] & 0xFF) << 8) |
      (digestArray[offset + 3] & 0xFF)
    );
  
    // 6. 取模得到指定位数的验证码
    code = code % Math.pow(10, digits);
  
    // 7. 格式化为指定长度的字符串（不足则前面补0）
    return code.toString().padStart(digits, '0');
  }
  
  // 如果是 Node.js 环境，导出函数
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = { generateTOTP };
  }
  
  // ES6 模块导出
  if (typeof window !== 'undefined') {
    (window as any).generateTOTP = generateTOTP;
  }
  
  // 导出函数供ES6模块使用
  export { generateTOTP };